package com.xyy.saas.payment.adpater.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.adpater.PaymentAdapter;
import com.xyy.saas.payment.adpater.config.BankConfigManager;
import com.xyy.saas.payment.adpater.fumin.client.OpenParameters;
import com.xyy.saas.payment.adpater.fumin.config.AreaCodeManager;
import com.xyy.saas.payment.adpater.fumin.config.FuminBankCodeConfig;
import com.xyy.saas.payment.adpater.fumin.constants.ApiNameConstants;
import com.xyy.saas.payment.adpater.fumin.enums.CurrencyEnum;
import com.xyy.saas.payment.cores.vo.FuminRecordBo;
import com.xyy.saas.payment.cores.vo.QueryFuminRecordBo;
import com.xyy.saas.payment.cores.vo.QueryAccountBalanceDO;
import com.xyy.saas.payment.adpater.fumin.pojo.domain.SeparateAccountDO;
import com.xyy.saas.payment.adpater.fumin.spring.SpringClient;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.constants.ChannelConstants;
import com.xyy.saas.payment.common.enums.FBankErrorMsgMappingEnum;
import com.xyy.saas.payment.common.enums.PictureEnum;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.*;
import com.xyy.saas.payment.cores.enums.*;
import com.xyy.saas.payment.cores.param.TransactionParam;
import com.xyy.saas.payment.cores.vo.*;
import com.xyy.saas.payment.dao.model.*;
import com.xyy.saas.payment.merchant.core.dto.*;
import com.xyy.saas.payment.merchant.core.service.AccountService;
import com.xyy.saas.payment.merchant.core.vo.Recipient;
import com.xyy.saas.payment.merchant.core.vo.SettleAccountDto;
import com.xyy.saas.payment.merchant.core.vo.TransactionInfoDto;
import com.xyy.saas.payment.merchant.core.vo.UserDto;
import com.xyy.saas.payment.payment.core.bo.RefundBO;
import com.xyy.saas.payment.payment.core.bo.WithdrawalBO;
import com.xyy.saas.payment.payment.core.dto.*;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.service.*;
import com.xyy.saas.payment.util.*;
import com.xyy.util.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import util.AESUtil;
import java.util.*;

/**
 * @author wangtianqi
 * @date 2020/8/25 14:08
 */
@Service
public class FuminAdapter implements PaymentAdapter {

    private static Logger LOGGER = LoggerFactory.getLogger(FuminAdapter.class);
    @Value("${fmBank.stop.switch:false}")
    private Boolean fmBankStopSwitch;

    @Value("${fmBank.aggregate.pay.url}")
    private String requestURL;

    @Value("${fmBank.callBack.pay}")
    private String callBackPayUrl;

    @Value("${fmBank.callBack.refund}")
    private String callBackRefundUrl;

    @Autowired
    private LeafUtil leafUtil;
    @Autowired
    private SpringClient springClient;
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private UserService userService;
    @Autowired
    private RedisClient redisClient;

    @Autowired
    private PrePayOrderService prePayOrderService;
    @Autowired
    private PayResultOrderService payResultOrderService;

    @Autowired
    private PayAccountService payAccountService;

    @Autowired
    private AreaCodeManager areaCodeManager;

    @Autowired
    private ThirdUserInfoService thirdUserInfoService;

    @Autowired
    private BankConfigManager bankConfigManager;
    @Autowired
    private FuminBankCodeConfig fuminBankCodeConfig;

    @Autowired
    private WxCommonAdpter wxCommonAdpter;

    @Autowired
    private AliCommonAdapter aliCommonAdapter;
    @Autowired
    private AccountService accountService;
    @Autowired
    private FuminAdapter2 fuminAdapter2;

    @Override
    public PrepayBo prepay(PrepayDto prepayDto) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        return prePayOrderService.prepayOrderByPrepayDto(prepayDto);
    }

    @Override
    public WithdrawalBO withdraw(WithdrawalDto withdrawalDto) throws PaymentException {
        UserInfo userInfo = userInfoService.queryUserInfoByAccountId(withdrawalDto.getUserId());
        //取出结算账户信息
        SettleAccountDto settleAccountDto = null;

        try {
            String jsonSettleAccount = AESUtil.decrypt(userInfo.getSettleInfo(), ApolloUtil.getProperty(Constants.USER_INFO_KEY));
            settleAccountDto = JSONUtils.parse2Object(jsonSettleAccount, SettleAccountDto.class);
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#withdraw userInfo decrypt error userId:{}", withdrawalDto.getUserId());
        }

        if(settleAccountDto == null) {
            throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST, "用户信息取出失败");
        }

        String merchantNo = bankConfigManager.getFuminBankMch(withdrawalDto.getBusinessIdType());
        JSONObject requestParam = new JSONObject();
        //日志流水号
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        //商户号"8808001002996654"
        requestParam.put("merchant_no", merchantNo);
        //商户侧id
        requestParam.put("cust_merchant_no", getAdapterAccountId(withdrawalDto.getUserId()));
        //币钟
        requestParam.put("currency", "CNY");
        //交易金额
        requestParam.put("trade_amount", AmountCastUtil.castFen2Yuan(withdrawalDto.getAmount()));
        //出账方账号
        requestParam.put("virtual_account_out", withdrawalDto.getChannelUserId());
        //出账方账户名
        requestParam.put("virtual_name_out", settleAccountDto.getRecipient().getName());
        //入账方账号
        requestParam.put("account_in", settleAccountDto.getRecipient().getAccount());
        //入账方姓名
        requestParam.put("name_in", settleAccountDto.getRecipient().getName());
        //入账方账户类型 1对私 2对公
        requestParam.put("acct_flag", AccountTypeEnum.CUSTOMER.getType().equals(settleAccountDto.getAccountType()) ? "1" : "2");
        //商户订单号
        requestParam.put("biz_channel_orderid", withdrawalDto.getWithdrawalNo());
        //银行简称 - 如果是BASE_BANK_SHORT_NAME则为联行号就不传 否则为简称传
        String bankCode = fuminBankCodeConfig.getBankCodeByName(settleAccountDto.getRecipient().getOpenBank());

        if(StringUtils.isNotBlank(bankCode) && !bankCode.equals(FmBankCode.BASE_BANK_SHORT_NAME)) {
            requestParam.put("bank_short_name", bankCode);
        }

        //交易备注
        requestParam.put("trade_remark", "");
        //交易手续费
        requestParam.put("poundage_amount", AmountCastUtil.castFen2Yuan(withdrawalDto.getFee()));

        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.trade.verifyWithdrawals").param(requestParam.toJSONString());
        String response;

        try {
            response = springClient.send(builder.build(), merchantNo);
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#withdraw requestParam:{} response:{}", JSON.toJSONString(requestParam), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#withdraw request:{} response:{}", JSONUtils.toJSON(requestParam), response);
        JSONObject responseParam = JSON.parseObject(response);
        Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

        if(!"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
        }

        Map<String, String> bodyParam = JSONUtils.parse2Map(responseParam.getString("body"), String.class, String.class);
        WithdrawalBO withdrawalBO = new WithdrawalBO();
        withdrawalBO.setUserName(settleAccountDto.getRecipient().getName());
        withdrawalBO.setChannelWithdrawNo(bodyParam.get("trade_no"));
        withdrawalBO.setStatus(bodyParam.get("trade_status"));
        withdrawalBO.setWithdrawalNo(withdrawalDto.getWithdrawalNo());
        return withdrawalBO;
    }

    @Override
    public WithdrawalBO withdrawAuth(WithdrawAuthDto withdrawAuthDto) throws PaymentException {
        return fuminAdapter2.withdrawAuth(withdrawAuthDto);
    }

    /**
     * 富民银行接口修改，走统一反洗钱接口
     * @param userDto
     * @return
     * @throws PaymentException
     */
    @Override
    public UserPicBo uploadPic(UserDto userDto) throws PaymentException {
        UserPicBo userPicBo = new UserPicBo();
        userPicBo.setAccountNo(userDto.getAccNo());
        userPicBo.setUser(userDto.getUserId());
        userPicBo.setThirdType(Constants.QIFUTONG);
        userPicBo.setType(userDto.getAccountType());
        return userPicBo;
    }

    @Override
    public SubAppBo createMerchant(UserDto userDto) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        LOGGER.info("FuminAdapter#createMerchant begin userDto:{}", JSON.toJSONString(userDto));
        String merchantNo = bankConfigManager.getFuminBankMch(userDto.getBusinessIdType());
        JSONObject requestParam = new JSONObject();
        //  法人
        ContactInfo contactInfo = userDto.getContactInfos().get(0);
        //  结算账户
        Recipient recipient = userDto.getSettleAccountDto().getRecipient();
        //	商户编号	Y
        requestParam.put("merchant_no", merchantNo);
        //	日志流水号	Y
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        //	调用方用户ID	Y
        requestParam.put("cust_merchant_no", getAdapterAccountId(userDto.getUserId()));
        //	企业名称	Y
        requestParam.put("ent_name", userDto.getName());
        //	证件类型	Y
        requestParam.put("enterprise_card_type", "03");
        //	证件号码	Y
        requestParam.put("enterprise_registration_no", userDto.getIdentityInfo().getId());
        //	证件注册日期	N
        requestParam.put("business_license_regist_date", userDto.getIdentityInfo().getValidFrom());
        //	证件到期日期	N
        requestParam.put("business_license_end_date", userDto.getIdentityInfo().getValidUntil());
        //	法人姓名	Y
        requestParam.put("corporation_name", contactInfo.getContactName());
        //	法人证件号码	Y
        requestParam.put("corporation_id_no", contactInfo.getContactCertNo());
        //	法人证件签发日期 yyyy-MM-dd	N
        requestParam.put("corporation_id_start_date", contactInfo.getContactCertValidFrom());
        //	法人证件到期日期 yyyy-MM-dd	N
        requestParam.put("corporation_id_end_date", contactInfo.getContactCertValidUntil());
        //  企业地址
        requestParam.put("business_location", userDto.getAddress());
        //图片类型
        requestParam.put("business_license_image_suffix", "jpg");
        //图片转码内容
        requestParam.put("business_license_image", getPicBase64(userDto, String.valueOf(PictureEnum.BUSINESS_CERT_PIC.getCode())));
        //法人身份证正面
        requestParam.put("corporation_card_front_image", getPicBase64(userDto, String.valueOf(PictureEnum.LEGAL_CERT_FRONT.getCode())));
        //法人身份证正面后缀
        requestParam.put("corporation_card_front_image_suffix", "jpg");
        //法人身份证反面
        requestParam.put("corporation_card_reverse_image", getPicBase64(userDto, String.valueOf(PictureEnum.LEGAL_CERT_BACK.getCode())));
        //法人身份证反面后缀
        requestParam.put("corporation_card_reverse_image_suffix", "jpg");
        //	手机号码	Y
        requestParam.put("cust_mobile", userDto.getMobile());
        //	结算卡业务类型	Y
        requestParam.put("business_type", "1");
        //	结算卡账户类型	Y
        requestParam.put("acct_type", "重庆富民银行".equals(recipient.getOpenBank()) ? 1 : 2);
        //	账户名	Y23536533523425223
        requestParam.put("account_name", recipient.getName());

        //	银行简称	联行号 选填
        String bankCode = fuminBankCodeConfig.getBankCodeByName(recipient.getOpenBank());

        // 如果Code是BASE_BANK_SHORT_NAME，则为联行号 否则为简称
        if(bankCode.equals(FmBankCode.BASE_BANK_SHORT_NAME)) {
            if(StringUtils.isBlank(recipient.getSubBankCode())) {
                throw new PaymentException(ResultCodeEnum.SUB_BANK_ERROR);
            }

            requestParam.put("bank_org_no", recipient.getSubBankCode());
        }
        else {
            requestParam.put("bank_code", bankCode);
        }

        //	企业成立日期	N
        requestParam.put("establish_date", userDto.getIdentityInfo().getValidFrom());
        //	短信验证码	N
        requestParam.put("active_code", JSONUtils.parse2Map(userDto.getExtraMap(), String.class, String.class).get("activeCode"));
        //	验证码交易流水号	N activeCodeSerialNo2为了兼容聚合支付开户场景
        String activeCodeSerialNo1 = null;

        try {
            activeCodeSerialNo1 = getActiveMsgInfoFromRedis(recipient.getMobile());
        }
        catch(Exception ex) {
            LOGGER.error(ex.getMessage() ,ex);
        }

        String activeCodeSerialNo2 = getActiveMsgInfoFromRedis(userDto.getMobile());
        requestParam.put("active_code_serial_no", !StringUtils.isEmpty(activeCodeSerialNo1) ? activeCodeSerialNo1 : activeCodeSerialNo2);
        //	法人证件类型	N
        requestParam.put("corporation_id_type", "0");
        //	实资金卡号	N
        requestParam.put("real_capital_acct", "");
        //	结算卡号	Y
        requestParam.put("acct", recipient.getAccount());
        //企业联系电话
        requestParam.put("enterprise_phone", userDto.getEnterprisePhone());
        //客户细类
        requestParam.put("category", userDto.getMerchantCategory());
        List<Map<String, String>> shareHolderList = new ArrayList<>();

        for(ShareHolderInfoVo sh : userDto.getShareHolderInfos()) {
            Map<String, String> shMap = new HashMap<>();
            shMap.put("shareholder_name", sh.getShareHolderName());

            if(CertTypeEnum.ID_CARD.getCode().equals(sh.getShareHolderCardType())) {
                shMap.put("shareholder_card_type", "0");
            }

            if(CertTypeEnum.ENTERPRISE_LICENSE.getCode().equals(sh.getShareHolderCardType())) {
                shMap.put("shareholder_card_type", "211");
            }

            if(CertTypeEnum.UNIFIED_CREDIT_CODE.getCode().equals(sh.getShareHolderCardType())) {
                shMap.put("shareholder_card_type", "252");
            }

            shMap.put("shareholder_card_no", sh.getShareHolderCardNo());
            shMap.put("shareholder_card_start_date", sh.getShareHolderCardStartDate());
            shMap.put("shareholder_card_end_date", sh.getShareHolderCardEndDate());
            shareHolderList.add(shMap);
        }

        //股东信息数组
        requestParam.put("shareholder_list", shareHolderList);
        List<Map<String, String>> beneficiaryList = new ArrayList<>();

        for(BeneficiaryInfoVo be : userDto.getBeneficiaryInfos()) {
            Map<String, String> beMap = new HashMap<>();
            beMap.put("beneficiary_card_type", "0");
            beMap.put("beneficiary_card_no", be.getBeneficiaryCardNo());
            beMap.put("beneficiary_card_start_date", be.getBeneficiaryCardStartDate());
            beMap.put("beneficiary_card_end_date", be.getBeneficiaryCardEndDate());
            beMap.put("beneficiary_phone", be.getBeneficiaryPhone());
            beMap.put("beneficiary_location", be.getBeneficiaryLocation());
            beMap.put("beneficiary_name", be.getBeneficiaryName());
            beneficiaryList.add(beMap);
        }

        //受益人信息数组
        requestParam.put("beneficiary_list", beneficiaryList);
        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v2.accountpayment.enterpriseAccountRegister").param(requestParam.toJSONString());
        String response;

        try {
            response = springClient.send(builder.build(), merchantNo);
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#createMerchant requestParam:{}", requestParam.toJSONString(), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#createMerchant requestParam:{} response:{}", requestParam.toJSONString(), response);
        JSONObject responseParam = JSON.parseObject(response);
        Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

        if(!"000000".equals(headParam.get("rsp_code"))) {
            LOGGER.warn("FuminAdapter#createMerchant requestParam:{}, response:{}", requestParam.toJSONString(), response);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, FBankErrorMsgMappingEnum.convert(headParam.get("rsp_msg")));
        }

        Map<String, String> bodyParam = JSONUtils.parse2Map(responseParam.getString("body"), String.class, String.class);
        SubAppBo subAppBo = new SubAppBo();
        subAppBo.setState(UserApplyStateEnum.SUCCEEDED);
        subAppBo.setAccountNo(bodyParam.get("cust_no"));
        subAppBo.setThirdType(Constants.QIFUTONG);
        userDto.setAccNo(subAppBo.getAccountNo());
        saveThirdUserId(userDto.getUserId(), bodyParam.get("balance_acc_no"));
        return subAppBo;
    }

    @Override
    public AccountModifyBo modifyMerchant(MerchantAccountModifyDto param) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        return fuminAdapter2.modifyMerchant(param);
    }

    @Override
    public SubAppBo createPersonal(UserDto userDto) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        String merchantNo = bankConfigManager.getFuminBankMch(userDto.getBusinessIdType());
        Recipient recipient = userDto.getSettleAccountDto().getRecipient();
        JSONObject requestParam = new JSONObject();
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        requestParam.put("merchant_no", merchantNo);
        requestParam.put("cust_merchant_no", getAdapterAccountId(userDto.getUserId()));
        requestParam.put("cust_name", userDto.getName());
        requestParam.put("acct", recipient.getAccount());
        requestParam.put("mobile_phone_num", userDto.getMobile());
        requestParam.put("cust_idt", "0");
        requestParam.put("cust_id_no", userDto.getIdentityInfo().getId());
        requestParam.put("card_start_date", userDto.getIdentityInfo().getValidFrom());
        requestParam.put("card_end_date", userDto.getIdentityInfo().getValidUntil());
        requestParam.put("professional", castPersonProfession(userDto.getIdentityInfo().getProfession()));
        requestParam.put("active_code", JSONUtils.parse2Map(userDto.getExtraMap(), String.class, String.class).get("activeCode"));
        requestParam.put("active_code_serial_no", getActiveMsgInfoFromRedis(userDto.getMobile()));
        //图片
        requestParam.put("id_card_front", getPicBase64(userDto, String.valueOf(PictureEnum.PERSONAL_CERT_FRONT.getCode())));
        requestParam.put("id_card_back", getPicBase64(userDto, String.valueOf(PictureEnum.PERSONAL_CERT_BACK.getCode())));

        //地区码
        String areaCode = areaCodeManager.getAreaCodeByArea(userDto.getAddress());

        if(StringUtils.isBlank(areaCode)) {
            throw new PaymentException(ResultCodeEnum.ADDRESS_ERROR);
        }

        requestParam.put("area_code", areaCode);
        requestParam.put("home_addr", userDto.getAddress());

        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.personalvirtualaccountopen.newPersonalAccount").param(requestParam.toJSONString());
        String response;

        try {
            response = springClient.send(builder.build(), merchantNo);
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#createPersonal userDto:{} response:{}", JSON.toJSONString(userDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#createPersonal request:{} response:{}", JSONUtils.toJSON(requestParam), response);
        JSONObject responseParam = JSON.parseObject(response);
        Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

        if(!"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, FBankErrorMsgMappingEnum.convert(headParam.get("rsp_msg")));
        }

        Map<String, String> bodyParam = JSONUtils.parse2Map(responseParam.getString("body"), String.class, String.class);
        SubAppBo subAppBo = new SubAppBo();
        subAppBo.setState(UserApplyStateEnum.SUCCEEDED);
        subAppBo.setAccountNo(bodyParam.get("account_no"));
        subAppBo.setThirdType(Constants.QIFUTONG);
        userDto.setAccNo(subAppBo.getAccountNo());
        saveThirdUserId(userDto.getUserId(), bodyParam.get("account_no"));
        return subAppBo;
    }

    @Override
    public AccountModifyBo modifyPersonal(PersonalAccountModifyDto param) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        return fuminAdapter2.modifyPersonal(param);
    }

    @Override
    public void personalCardBind(PersonalCardBindDto personalCardBindDto) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        JSONObject requestParam = new JSONObject();
        String merchantNo = bankConfigManager.getFuminBankMch(personalCardBindDto.getBusinessIdType());
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        requestParam.put("merchant_no", merchantNo);
        requestParam.put("cust_merchant_no", personalCardBindDto.getUserId());
        requestParam.put("biz_channel_orderid", leafUtil.getSnowflakeId());
        requestParam.put("cust_mobile", personalCardBindDto.getMobile());
        requestParam.put("cust_name", personalCardBindDto.getName());
        requestParam.put("cust_idt", "0");
        requestParam.put("cust_id_no", personalCardBindDto.getCardId());
        requestParam.put("mobile_phone_num", personalCardBindDto.getMobile());
        requestParam.put("acct", personalCardBindDto.getAccount());
        requestParam.put("active_code", JSONUtils.parse2Map(personalCardBindDto.getExtra(), String.class, String.class).get("activeCode"));
        requestParam.put("active_code_serial_no", getActiveMsgInfoFromRedis(personalCardBindDto.getMobile()));

        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.card.bind").param(requestParam.toJSONString());
        String response;

        try {
            response = springClient.send(builder.build(), merchantNo);
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#personalCardBind personalCardBindDto:{} response:{}", JSON.toJSONString(personalCardBindDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#personalCardBind request:{} response:{}", JSONUtils.toJSON(requestParam), response);
        JSONObject responseParam = JSON.parseObject(response);
        Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

        if(!"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
        }
    }

    @Override
    public String merchantCardBind(MerchantCardBindDto merchantCardBindDto) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        JSONObject requestParam = new JSONObject();
        String merchantNo = bankConfigManager.getFuminBankMch(merchantCardBindDto.getBusinessIdType());
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        requestParam.put("merchant_no", merchantNo);
        requestParam.put("cust_merchant_no", merchantCardBindDto.getUserId());
        requestParam.put("biz_channel_orderid", leafUtil.getSnowflakeId());
        requestParam.put("cust_mobile", merchantCardBindDto.getMobile());
        requestParam.put("card_name", merchantCardBindDto.getSettleName());
        requestParam.put("tag", "02");
        requestParam.put("acct", merchantCardBindDto.getAccount());
        requestParam.put("acct_type", "重庆富民银行".equals(merchantCardBindDto.getOpenBank()) ? 1 : 2);

        // 银行简称	联行号 选填 -- 如果Code是纯数字，则为联行号 否则为简称
        if(merchantCardBindDto.getOpenBankCode().equals(FmBankCode.BASE_BANK_SHORT_NAME)) {
            requestParam.put("bank_org_no", merchantCardBindDto.getBranchBankCd());
        }
        else {
            requestParam.put("bank_code", merchantCardBindDto.getOpenBankCode());
        }

        requestParam.put("business_type", "1");
        requestParam.put("active_code", JSONUtils.parse2Map(merchantCardBindDto.getExtra(), String.class, String.class).get("activeCode"));
        requestParam.put("active_code_serial_no", getActiveMsgInfoFromRedis(merchantCardBindDto.getMobile()));

        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.card.enterprise.bind").param(requestParam.toJSONString());
        String response;

        try {
            response = springClient.send(builder.build(), merchantNo);
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#merchantCardBind merchantCardBindDto:{} response:{}", JSON.toJSONString(merchantCardBindDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#merchantCardBind request:{} response:{}", JSONUtils.toJSON(requestParam), response);
        JSONObject responseParam = JSON.parseObject(response);
        Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

        if(!"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
        }

        return "";
    }

    @Override
    public void unBindCard(UnBindCardDto unBindCardDto) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        JSONObject requestParam = new JSONObject();
        String merchantNo = bankConfigManager.getFuminBankMch(unBindCardDto.getBusinessIdType());
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        requestParam.put("merchant_no", merchantNo);
        requestParam.put("cust_merchant_no", unBindCardDto.getUserId());
        requestParam.put("card_type", "1");
        requestParam.put("original_card_no", unBindCardDto.getAccount());
        requestParam.put("cust_name", unBindCardDto.getAccountName());

        if(Constants.PERSONAL.equals(unBindCardDto.getAccountType())) {
            requestParam.put("mobile_phone_num", unBindCardDto.getMobile());
            requestParam.put("cust_idt", "0");
            //	银行简称	Y
            requestParam.put("cust_id_no", unBindCardDto.getCardId());
        }

        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.account.unbindCard").param(requestParam.toJSONString());
        String response;

        try {
            response = springClient.send(builder.build(), merchantNo);
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#unBindCard unBindCardDto:{} response:{}", JSON.toJSONString(unBindCardDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#unBindCard request:{} response:{}", JSONUtils.toJSON(requestParam), response);
        JSONObject responseParam = JSON.parseObject(response);
        Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

        if(!"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
        }
    }

    @Override
    @Retryable
    public UserBo queryAccount(QueryAccountDto queryAccountDto) throws PaymentException {
        String merchantNo = bankConfigManager.getFuminBankMch(queryAccountDto.getBusinessIdType());
        JSONObject requestParam = new JSONObject();
        //日志流水号
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        //商户号
        requestParam.put("merchant_no", merchantNo);
        requestParam.put("cust_merchant_no", getAdapterAccountId(queryAccountDto.getUserId()));
        requestParam.put("account_no", queryAccountDto.getThirdUserId());
        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.account.queryAccountBal").param(requestParam.toJSONString());
        String response;

        try {
            response = springClient.send(builder.build(), merchantNo);
        } catch (Exception e) {
            LOGGER.error("FuminAdapter#queryAccount requestParam:{} response:{}", JSON.toJSONString(requestParam), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#queryAccount request:{} response:{}", JSONUtils.toJSON(requestParam), response);
        JSONObject responseParam = JSON.parseObject(response);
        Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

        if(!"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
        }

        Map<String, String> bodyParam = JSONUtils.parse2Map(responseParam.getString("body"), String.class, String.class);
        return UserBo.builder().
                availableBalance(AmountCastUtil.castYuan2FenLong(bodyParam.get("availableBalance"))).
                withdrawableBalance(AmountCastUtil.castYuan2FenLong(bodyParam.get("availableBalance"))).
                frozenAmount(AmountCastUtil.castYuan2FenLong(bodyParam.get("frozenAmount"))).
                build();
    }

    @Override
    public QueryResultBo queryRefundStatus(RefundOrder refundOrder) throws PaymentException {
        PayAccount payAccount = payAccountService.getPayAccountByAccountId(refundOrder.getUserId());

        if(null == payAccount) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }

        QueryResultBo queryResultBo = queryTransactionStatus(QueryTransactionStatusDto.
                build(payAccount.getBusinessIdType(), refundOrder.getRefundNo(),
                        QueryTransactionStatusDto.TRADE_TYPE_REFUND, refundOrder.getUserId(), refundOrder.getCreateTime()));

        if(queryResultBo == null) {
            return null;
        }

        return QueryResultBo.build(QueryResultBo.convertStatus(queryResultBo.getStatus()),
                queryResultBo.getSuccessTime(), queryResultBo.getAmount());
    }

    /**
     * 检查交易状态
     */
    @Override
    public QueryResultBo queryTransactionStatus(QueryTransactionStatusDto queryTransactionStatusDto) throws PaymentException {
        SeparateAccountDO separateAccountDO = new SeparateAccountDO();
        separateAccountDO.setBusinessOrderType(queryTransactionStatusDto.getBusinessOrderType());
        separateAccountDO.setSeparateNo(queryTransactionStatusDto.getTransactionNo());
        TransactionInfoDto transactionInfoDto = null;

        try {
            transactionInfoDto = queryTransactionInfo(separateAccountDO);
        }
        catch(PaymentException e) {
            return null;
        }

        return QueryResultBo.build(transactionInfoDto.getTradeStatus(), DateUtil.strToLong(transactionInfoDto.getFinishTime()),
                transactionInfoDto.getTradeNo(), AmountCastUtil.castYuan2Fen(transactionInfoDto.getAmount()));
    }

    /**
     * 查询交易状态
     * @param param
     * @return
     * @throws PaymentException
     */
    @Override
    public TransactionResultBO queryTransaction(TransactionParam param) throws PaymentException {
        LOGGER.info("FuminAdapter#queryTransactionInfo 入参信息：{} ", JSONUtils.toJSON(param));
        JSONObject jsonObject = new JSONObject();

        try {
            String merchantNo = bankConfigManager.getFuminBankMch(param.getBusinessOrderType());
            //商户号
            jsonObject.put("merchant_no", merchantNo);
            //日志流水号
            jsonObject.put("client_serial_no", leafUtil.getSnowflakeId());
            //原交易订单号
            jsonObject.put("biz_channel_orderid", param.getTradeNo());
            OpenParameters.Builder builder = new OpenParameters.Builder();
            builder.apiName(ApiNameConstants.queryTransactionInfo)
                    .param(jsonObject.toJSONString());
            String response = springClient.send(builder.build(), merchantNo);
            LOGGER.info("FuminAdapter#queryTransactionInfo request:{} response:{} ", JSONUtils.toJSON(jsonObject), response);
            JSONObject responseParam = JSON.parseObject(response);
            Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

            if(!"000000".equals(headParam.get("rsp_code"))) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
            }

            String body = responseParam.getString("body");

            if(StringUtil.isEmpty(body)) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, "FuminAdapter#queryTransactionInfo response body is null");
            }

            JSONObject bodyParam = JSONUtils.parse2Object(body, JSONObject.class);
            TransactionResultBO transactionResultBO = buildTransactionResultBO(bodyParam);
            transactionResultBO.setTradeNo(param.getTradeNo());

            if(TransactionTypeEnum.PAY.getCode().equals(param.getTransactionType()) || TransactionTypeEnum.REFUND.getCode().equals(param.getTransactionType())) {
                FBankPowerIdEnum power = FBankPowerIdEnum.getPowerId(param.getChannel(), param.getPayMode());
                // 明确富民
                transactionResultBO.setChannel(ChannelConstants.CHANNEL_FUMIN_BANK_NAME + "_" + power.getChannel());
            }

            return transactionResultBO;
        }
        catch(PaymentException e) {
            throw e;
        }
        catch(Exception e) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
    }

    @Override
    public PayBo pay(PayDto payDto) throws PaymentException {
        LOGGER.info("FuminAdapter#pay payDto：{}", JSONObject.toJSONString(payDto));
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        if(bankConfigManager.isFuminBankAggregatePay(payDto.getBusinessOrderType(), payDto.getReceiverId(), payDto.getPayMode())) {
            return aggregatePay(payDto);
        }
        else {
            String channel = payDto.getChannel();

            if(channel.contains(ChannelEnum.WX.getCode())) {
                return wxCommonAdpter.pay(payDto);
            }

            if(channel.contains(ChannelEnum.ALIPAY.getCode())) {
                return aliCommonAdapter.pay(payDto);
            }
        }

        throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, "无法识别的支付渠道");
    }

    @Override
    public RefundBO refund(RefundDto refundDto) throws PaymentException {
        LOGGER.info("FuminAdapter#refund 传入参数 refundDto：{}", JSONObject.toJSONString(refundDto));
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        String merchantNo = bankConfigManager.getFuminBankMch(refundDto.getBusinessOrderType());
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(getAdapterAccountId(getRefundAccountId(refundDto.getPayNo(), refundDto.getChannel())));

        if(Constants.PINGAN.equals(thirdUserInfo.getThirdType())) {
            thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(thirdUserInfo.getUserId() + PayAccount.SUFFIX_DELETE);
        }

        JSONObject requestParam = new JSONObject();
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        requestParam.put("biz_channel_orderid", refundDto.getRefundNo());
        requestParam.put("merchant_no", merchantNo);
        requestParam.put("original_trade_no", refundDto.getPayNo());
        requestParam.put("txn_amt", AmountCastUtil.castFen2Yuan(refundDto.getAmount()));
        requestParam.put("trade_remark", refundDto.getDescription());//交易备注
        requestParam.put("prorate_info", thirdUserInfo.getThirdUserId() + ":" + requestParam.get("txn_amt"));//分账域
        requestParam.put("notify_url", callBackRefundUrl);

        try {
            OpenParameters.Builder builder = new OpenParameters.Builder();
            builder.apiName("com.fbank.openapi.api.v1.trade.aggregateRefund")
                    .param(requestParam.toJSONString());
            String response = springClient.send(builder.build(), merchantNo);
            LOGGER.info("FuminAdapter#refund the TestTransfer request:{} response:{} ", JSONUtils.toJSON(requestParam), response);
            JSONObject responseParam = JSON.parseObject(response);
            Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

            if(!"000000".equals(headParam.get("rsp_code"))) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
            }

            JSONObject bodyParam = JSONUtils.parse2Object(responseParam.getString("body"), JSONObject.class);
            RefundBO refundBO = new RefundBO();
            refundBO.setRefundNo(refundDto.getRefundNo());
            refundBO.setChannelRefundNo(bodyParam.getString("trade_no"));
            refundBO.setStatus(RefundOrder.STATUS_IN);
            return refundBO;
        }
        catch(PaymentException e) {
            LOGGER.error("FuminAdapter#refund request:{} ", JSON.toJSONString(requestParam), e);
            throw e;
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#refund request:{} ", JSON.toJSONString(requestParam), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
    }

    @Override
    public void sendActiveCodeByPhoneNum(ActiveCodeDto activeCodeDto) throws PaymentException {
        // 账户修改发短信走2.0
        if(AccountTypeEnum.CUSTOMER_MODIFY.getType().equals(activeCodeDto.getAccountType()) ||
                AccountTypeEnum.BUSINESS_MODIFY.getType().equals(activeCodeDto.getAccountType()))
        {
            fuminAdapter2.sendActiveCodeByPhoneNum(activeCodeDto);
            return;
        }

        JSONObject requestParam = new JSONObject();
        String merchantNo = bankConfigManager.getFuminBankMch(activeCodeDto.getBusinessIdType());
        //日志流水号
        requestParam.put("client_serial_no", leafUtil.getSnowflakeId());
        //商户号
        requestParam.put("merchant_no", merchantNo);

        //手机号
        // 对公绑卡不需要传手机号
        if(!AccountTypeEnum.BUSINESS_BIND.getType().equals(activeCodeDto.getAccountType())) {
            requestParam.put("cust_mobile", activeCodeDto.getPhoneNum());
        }

        requestParam.put("cust_merchant_no", getAdapterAccountId(activeCodeDto.getAccountId()));
        //验证码类型 02 个人实名 04 企业实名
        requestParam.put("operate_type", AccountTypeEnum.getOperateType(activeCodeDto.getAccountType()));
        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.msgcode.sendmsgcode").param(requestParam.toJSONString());
        String response;

        try {
            response = springClient.send(builder.build(), merchantNo);
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#sendActiveCodeByPhoneNum activeCodeDto:{} response:{}", JSON.toJSONString(activeCodeDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#sendActiveCodeByPhoneNum request:{} response:{}", JSONUtils.toJSON(requestParam), response);
        JSONObject responseParam = JSON.parseObject(response);
        Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

        if(!"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
        }

        Map<String, String> bodyParam = JSONUtils.parse2Map(responseParam.getString("body"), String.class, String.class);
        LOGGER.info("FuminAdapter#sendActiveCodeByPhoneNum activeCodeDto:{} response:{}", JSON.toJSONString(activeCodeDto), response);
        // 验证码有效期10分钟
        redisClient.set(Constants.ACTIVE_CODE_MSG_INFO + activeCodeDto.getPhoneNum(), bodyParam.get("msgInfo"), 800L);
    }

    /**
     * 1.聚合支付个人进件
     * 2.聚合支付企业进件
     */
    @Override
    public CreateAllPayBo createAllPay(CreateAllPayDto createAllPayDto) throws PaymentException {
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        // 0.补充参数
        createAllPayDto.setMerchantNo(bankConfigManager.getFuminBankMch(createAllPayDto.getBusinessIdType()));
        createAllPayDto.setClientSerialNo(leafUtil.getSnowflakeId());

        LOGGER.info("FuminAdapter#createAllPay createAllPayDto：{}", JSONObject.toJSONString(createAllPayDto));

        // 1.上传基本信息
        OpenParameters.Builder builder = new OpenParameters.Builder();
        String requestParam = createAllPayDto.getJSONStringParam();
        builder.apiName("com.fbank.open-api.api.v2.allpay.createAllPay").param(requestParam);
        JSONObject response;

        try {
            response = JSON.parseObject(springClient.send(builder.build(), createAllPayDto.getMerchantNo()));
        }
        catch(Exception ex) {
            LOGGER.error("FuminAdapter#createAllPay createAllPayDto:{}", JSON.toJSONString(createAllPayDto), ex);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#createAllPay, requestParam:{}, response:{}", requestParam, response.toJSONString());
        Map<String, String> headParam = JSONUtils.parse2Map(response.getString("head"), String.class, String.class);

        /**
         * 000000   成功
         * CM100001 重复提交
         */
        if(headParam == null || (!"000000".equals(headParam.get("rsp_code")) && !"CM100001".equals(headParam.get("rsp_code")))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR,
                    headParam == null ? ResultCodeEnum.CHANNEL_CONNECT_ERROR.getDisplayMsg() : FBankErrorMsgMappingEnum.convert(headParam.get("rsp_msg")));
        }

        // 2.上传图片信息
        batchUploadImg(BatchUploadImgDto.build(createAllPayDto.getMerchantNo(), createAllPayDto.getCustMerchantNo(),
                leafUtil.getSnowflakeId(), PicUtil.toZiP(createAllPayDto.getPicMap(), createAllPayDto.getUserId())));
        // TODO 根据返回值设置返回参数
        CreateAllPayBo result = new CreateAllPayBo();
        return result;
    }

    /**
     * 开通支付状态查询
     */
    @Override
    public Map<String, Boolean> queryOpenPay(String accountId, String businessIdType) throws PaymentException {
        LOGGER.info("FuminAdapter#queryOpenPay accountId:{}, businessIdType:{}", accountId, businessIdType);
        long beginTime = System.currentTimeMillis();
        String merchantNo = bankConfigManager.getFuminBankMch(businessIdType);
        JSONObject object = new JSONObject();
        object.put("merchant_no", merchantNo);
        object.put("cust_merchant_no", getAdapterAccountId(accountId));
        object.put("client_serial_no", leafUtil.getSnowflakeId());
        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.allpay.queryPayChannel").param(object.toJSONString());
        JSONObject response;

        try {
            response = JSON.parseObject(springClient.send(builder.build(), merchantNo));
        }
        catch(Exception ex) {
            LOGGER.error("FuminAdapter#queryOpenPay merchantNo:" + merchantNo + ",accountId:" + accountId + ",V:" + businessIdType, ex);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#queryOpenPay response:{} costTime:{}", response.toJSONString() , System.currentTimeMillis() - beginTime);
        Map<String, String> headParam = JSONUtils.parse2Map(response.getString("head"), String.class, String.class);

        if(headParam == null || !"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR,
                    headParam == null ? ResultCodeEnum.CHANNEL_CONNECT_ERROR.getDisplayMsg() : headParam.get("rsp_msg"));
        }

        JSONArray array = response.getJSONObject("body").getJSONArray("status_data");
        Map<String, Boolean> result = new HashMap<>();

        for(int i = 0; i < array.size(); i++) {
            JSONObject o = array.getJSONObject(i);
            result.put(o.getString("power_id"), "SUCCESS".equals(o.getString("open_pay_status")));
        }

        return result;
    }

    /**
     * 账户间转账
     */
    @Override
    public SeparateAccountsBo separateAccountBetweenUser(SeparateAccountDO separateAccountDO) throws PaymentException {
        LOGGER.info("FuminAdapter#separateAccounts 入参信息：{} ", JSONUtils.toJSON(separateAccountDO));
        JSONObject jsonObject = new JSONObject();

        try {
            String merchantNo = bankConfigManager.getFuminBankMch(separateAccountDO.getBusinessOrderType());
            //商户号
            jsonObject.put("merchant_no", merchantNo);
            //商户订单号
            jsonObject.put("biz_channel_orderid", separateAccountDO.getSeparateNo());
            //交易金额
            jsonObject.put("trade_amount", AmountCastUtil.castFen2Yuan(separateAccountDO.getTradeAmount()));
            //币种
            jsonObject.put("currency", CurrencyEnum.RMB.getCode());
            //入账方账号
            jsonObject.put("account_in", separateAccountDO.getAccountIn());
            //ƒ入账方姓名
            jsonObject.put("name_in", separateAccountDO.getNameIn());
            //入账方商户侧id
            jsonObject.put("cust_no_in", getAdapterAccountId(separateAccountDO.getCustNoIn()));
            //出账方账号
            jsonObject.put("account_out", separateAccountDO.getAccountOut());
            //出账方姓名
            jsonObject.put("name_out", separateAccountDO.getNameOut());
            //调用方id
            jsonObject.put("cust_merchant_no", getAdapterAccountId(separateAccountDO.getCustMerchantNo()));
            //日志流水号
            jsonObject.put("client_serial_no", leafUtil.getSnowflakeId());

            if (separateAccountDO.getTradeRemark() != null) {
                //备注
                jsonObject.put("trade_remark", separateAccountDO.getTradeRemark());
            }

            OpenParameters.Builder builder = new OpenParameters.Builder();
            builder.apiName(ApiNameConstants.separateAccounts).param(jsonObject.toJSONString());
            String response = springClient.send(builder.build(), merchantNo);
            LOGGER.info("FuminAdapter#separateAccounts the TestTransfer request:{} response:{} ", JSONUtils.toJSON(jsonObject), response);
            JSONObject responseParam = JSON.parseObject(response);
            Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

            if(!"000000".equals(headParam.get("rsp_code"))) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
            }
        }
        catch(PaymentException e) {
            LOGGER.error("FuminAdapter#separateAccounts error separateAccountDO:{}，Exception:{}", JSON.toJSONString(jsonObject), e);
            throw e;
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#separateAccounts error separateAccountDO:{}，Exception:{}", JSON.toJSONString(jsonObject), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        LOGGER.info("FuminAdapter#separateAccountBetweenUser user separate finish separateAccountDO:{}", JSON.toJSONString(separateAccountDO));
        return SeparateAccountsBo.build(separateAccountDO.getSeparateNo());
    }

    /**
     * 查询账户流水
     */
    @Override
    public QueryFuminRecordBo queryBalanceRecord(QueryAccountBalanceDO queryAccountBalanceDO) throws PaymentException {
        LOGGER.info("FuminAdapter#queryBalanceRecord 入参信息：{} ", JSONObject.toJSONString(queryAccountBalanceDO));
        JSONObject jsonObject = new JSONObject();

        try {
            String merchantNo = bankConfigManager.getFuminBankMch(queryAccountBalanceDO.getBusinessOrderType());
            //商户号
            jsonObject.put("merchant_no", merchantNo);
            //调用方id
            jsonObject.put("cust_merchant_no", getAdapterAccountId(queryAccountBalanceDO.getUserId()));
            //日志流水号
            jsonObject.put("client_serial_no", leafUtil.getSnowflakeId());
            //当前页
            jsonObject.put("page_no", queryAccountBalanceDO.getPageNo());
            //每页数量
            jsonObject.put("page_size", 20);
            //企富通账户号
            jsonObject.put("account_no", queryAccountBalanceDO.getThirdUserId());
            //查询开始时间
            jsonObject.put("trade_start_date", queryAccountBalanceDO.getBeginTime());
            //查询结束时间
            jsonObject.put("trade_end_date", queryAccountBalanceDO.getEndTime());

            OpenParameters.Builder builder = new OpenParameters.Builder();
            builder.apiName("com.fbank.open-api.api.v1.accountpayment.balanceChangeRecordQuery")
                    .param(jsonObject.toJSONString());
            String response = springClient.send(builder.build(), merchantNo);
            LOGGER.info("FuminAdapter#queryBalanceRecord the TestTransfer request:{} response:{} ", JSONUtils.toJSON(jsonObject), response);
            JSONObject responseParam = JSON.parseObject(response);
            Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

            if(!"000000".equals(headParam.get("rsp_code"))) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
            }

            JSONObject bodyParam = JSONUtils.parse2Object(responseParam.getString("body"), JSONObject.class);
            QueryFuminRecordBo queryFuminRecordBo = buildQueryFuminRecordBo(bodyParam);
            return queryFuminRecordBo;
        }
        catch(PaymentException e) {
            LOGGER.error("FuminAdapter#queryBalanceRecord error queryAccountBalanceDO:{}", JSON.toJSONString(jsonObject), e);
            throw e;
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#queryBalanceRecord error queryAccountBalanceDO:{}", JSON.toJSONString(jsonObject), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
    }

    @Override
    public FrozenBo frozenBalance(FrozenDto frozenDto) throws PaymentException {
        LOGGER.info("FuminAdapter#frozenBalance, frozenDto={}", JSONObject.toJSONString(frozenDto));

        try {
            String merchantNo = bankConfigManager.getFuminBankMch(frozenDto.getBusinessOrderType());
            ThirdUserInfo thirdUserInfo =  thirdUserInfoService.getThirdUserInfoByUserId(frozenDto.getAccountId());

            if(thirdUserInfo == null) {
                throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
            }

            JSONObject jsonObject = new JSONObject();
            //商户号
            jsonObject.put("merchant_no", merchantNo);
            //调用方id
            jsonObject.put("cust_merchant_no", getAdapterAccountId(frozenDto.getAccountId()));
            //日志流水号
            jsonObject.put("client_serial_no", leafUtil.getSnowflakeId());
            // 业务订单号
            jsonObject.put("biz_channel_orderid", frozenDto.getBizChannelOrderId());
            // 账户号
            jsonObject.put("account_no", thirdUserInfo.getThirdUserId());
            // 冻结金额 单位是元，保留两位小数 0.00
            jsonObject.put("amount", frozenDto.getAmount());
            // 币种
            jsonObject.put("currency", frozenDto.getCurrency());

            OpenParameters.Builder builder = new OpenParameters.Builder();
            builder.apiName("com.fbank.open-api.api.v2.accountpayment.capitalFrozen")
                    .param(jsonObject.toJSONString());
            String response = springClient.send(builder.build(), merchantNo);
            LOGGER.info("FuminAdapter#frozenBalance, frozenDto request:{} response:{} ", JSONUtils.toJSON(jsonObject), response);
            JSONObject responseParam = JSON.parseObject(response);
            Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

            if(!"000000".equals(headParam.get("rsp_code"))) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
            }

            JSONObject bodyParam = JSONUtils.parse2Object(responseParam.getString("body"), JSONObject.class);
            String handleStatus = bodyParam.getString("handle_status");

            if("2".equals(handleStatus)) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, bodyParam.getString("message"));
            }

            /**
             * TODO 判断冻结是否成功
             */

            return FrozenBo.build(bodyParam.getString("biz_channel_orderid"));
        }
        catch(Exception ex) {
            LOGGER.error("FuminAdapter#frozenBalance, frozenDto=" + JSONObject.toJSONString(frozenDto), ex);

            if(ex instanceof PaymentException) {
                throw  (PaymentException) ex;
            }

            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
    }

    @Override
    public UnfrozenBo unfrozenBalance(UnfrozenDto unfrozenDto) throws PaymentException {
        LOGGER.info("FuminAdapter#unfrozenBalance unfrozenDto={}", JSONObject.toJSONString(unfrozenDto));

        try {
            String merchantNo = bankConfigManager.getFuminBankMch(unfrozenDto.getBusinessOrderType());
            ThirdUserInfo thirdUserInfo =  thirdUserInfoService.getThirdUserInfoByUserId(unfrozenDto.getAccountId());

            if(thirdUserInfo == null) {
                throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
            }

            JSONObject jsonObject = new JSONObject();
            //商户号
            jsonObject.put("merchant_no", merchantNo);
            //调用方id
            jsonObject.put("cust_merchant_no", getAdapterAccountId(unfrozenDto.getAccountId()));
            //日志流水号
            jsonObject.put("client_serial_no", leafUtil.getSnowflakeId());
            // 解冻业务单号
            jsonObject.put("biz_channel_orderid", unfrozenDto.getBizChannelOrderId());
            // 解冻业务单号
            jsonObject.put("origin_biz_channel_orderid", unfrozenDto.getOriginBizChannelOrderId());
            // 账户号
            jsonObject.put("account_no", thirdUserInfo.getThirdUserId());
            // 解冻标志 1 全部解冻 2 部分解冻
            jsonObject.put("thawrozen_flag", unfrozenDto.getThawrozenFlag());
            // 解冻金额 解冻标识为部分解冻时必填
            jsonObject.put("amount", unfrozenDto.getAmount());
            // 币种
            jsonObject.put("currency", unfrozenDto.getCurrency());

            OpenParameters.Builder builder = new OpenParameters.Builder();
            builder.apiName("com.fbank.open-api.api.v2.accountpayment.capitalThaw")
                    .param(jsonObject.toJSONString());
            String response = springClient.send(builder.build(), merchantNo);
            LOGGER.info("FuminAdapter#unfrozenBalance, unfrozenDto:{} request:{} response:{} ", JSONUtils.toJSON(unfrozenDto), JSONUtils.toJSON(jsonObject), response);
            JSONObject responseParam = JSON.parseObject(response);
            Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

            if(!"000000".equals(headParam.get("rsp_code"))) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
            }

            JSONObject bodyParam = JSONUtils.parse2Object(responseParam.getString("body"), JSONObject.class);
            String handleStatus = bodyParam.getString("handle_status");

            if("2".equals(handleStatus)) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, bodyParam.getString("message"));
            }

            /**
             * TODO 判断解冻是否成功
             */

            return UnfrozenBo.build(bodyParam.getString("biz_channel_orderid"));
        }
        catch(Exception ex) {
            LOGGER.error("FuminAdapter#unfrozenBalance, unfrozenDto=" + JSONObject.toJSONString(unfrozenDto), ex);

            if(ex instanceof PaymentException) {
                throw  (PaymentException) ex;
            }

            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
    }

    /**
     * 聚合支付
     * @param payDto
     * @return
     * @throws PaymentException
     */
    private PayBo aggregatePay(PayDto payDto) throws PaymentException {
        LOGGER.info("FuminAdapter#aggregatePay payDto：{}", JSONObject.toJSONString(payDto));
        if (fmBankStopSwitch) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }
        if(Objects.equals(BusinessIdTypeEnum.YIKUAIQIAN_FM.getType(), payDto.getBusinessOrderType())) {
            throw new PaymentException(ResultCodeEnum.FM_CHANNEL_CLOSED);
        }

        PayBo temp = redisClient.get(payDto.getPayNo(), PayBo.class);

        if(temp != null) {
            LOGGER.info("FuminAdapter#aggregatePay end get from redis appPayBo:{}", JSON.toJSONString(temp));
            return temp;
        }

        JSONObject requestParam = new JSONObject();
        String merchantNo = bankConfigManager.getFuminBankMch(payDto.getBusinessOrderType());
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(getAdapterAccountId(getPayeeAccountId(payDto.getReceiverId())));
        //订单总金额
        requestParam.put("amount", payDto.getAmount());
        //商品名称
        requestParam.put("subject", payDto.getSubject());
        //订单描述
        requestParam.put("body", payDto.getBody());
        //商户订单号
        requestParam.put("mchntOrderNo", payDto.getPayNo());
        //接口版本号
        requestParam.put("version", "api_1.0");
        //用户真实ip
        requestParam.put("clientIp", Constants.DEFAULT_CLIENT_IP);
        // 异步通知地址，保证外网可以访问
        requestParam.put("notifyUrl", callBackPayUrl);
        //分账域
        requestParam.put("propateInfo", thirdUserInfo.getThirdUserId() + ":" + payDto.getAmount());
        //描述
        requestParam.put("description", payDto.getChannel().split("_")[1]);
        //扩展
        requestParam.put("extra", thirdUserInfo.getThirdUserId());
        FBankPowerIdEnum powerIdEnum = FBankPowerIdEnum.getPowerId(payDto.getChannel(), payDto.getPayMode());
        //支付方式
        requestParam.put("payPowerId", powerIdEnum.getPowerId());

        if(payDto.getChannel().contains(ChannelEnum.WX.getCode())) {
            //用户在微信公众号或者微信小程序下的唯一标识
            requestParam.put("subOpenId", payDto.getMap().get("openId"));
            // 商户传入此appid时，则使用此appid;如不传，则使用默认的appid (是否有默认的appid,需要富民运营配置)
            requestParam.put("subAppId", FBankPowerIdEnum.getAppIdInfo(payDto.getBusinessOrderType(), powerIdEnum).getKey());
        }
        else if(payDto.getChannel().contains(ChannelEnum.ALIPAY.getCode())) {
            //用户在京东、支付宝平台唯一id（支付宝固定二维码、京东支付用到）
            requestParam.put("openId", payDto.getMap().get("buyerId"));
        }

        PayBo payBo = new PayBo();

        try {
            TreeMap<String, Object> resultTreeMap = springClient.sendToChannel(requestParam, requestURL, merchantNo);

            // 预下单成功
            if(resultTreeMap.get("code").equals("10000")) {
                if(payDto.getChannel().contains(ChannelEnum.WX.getCode())) {
                    payBo.setPayLoad(WxUtil.convertWXPayLoad((String)resultTreeMap.get("prepay_info")));
                }
                else if(payDto.getChannel().contains(ChannelEnum.ALIPAY.getCode())) {
                    payBo.setPayLoad((String)resultTreeMap.get("bankTransactionId"));
                }

                payBo.setPayNO(payDto.getPayNo());
            }
            // 预下单失败
            else {
                throw new PaymentException(resultTreeMap.get("code").toString(), resultTreeMap.get("msg").toString());
            }
        }
        catch(PaymentException e) {
            LOGGER.error("FuminAdapter#aggregatePay payDto=" + JSON.toJSONString(payDto), e);
            throw e;
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#aggregatePay payDto=" + JSON.toJSONString(payDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        redisClient.set(payDto.getPayNo(), payBo, PayTimeExpireUtil.handleTimeExpire(payDto.getChannel(), payDto.getPayMode(), payDto.getTimeExpire()));
        LOGGER.info("FuminAdapter#aggregatePay request:{} payBo:{}", JSONUtils.toJSON(requestParam), JSONObject.toJSONString(payBo));
        return payBo;
    }

    private String getActiveMsgInfoFromRedis(String phoneNum) throws PaymentException {
        String msgInfo = redisClient.get(Constants.ACTIVE_CODE_MSG_INFO + phoneNum);

        if(msgInfo == null) {
            throw new PaymentException(ResultCodeEnum.ACTIVE_CODE_ERROR);
        }

        return msgInfo;
    }

    private TransactionResultBO buildTransactionResultBO(JSONObject bodyParam) {
        TransactionResultBO bo = new TransactionResultBO();
        bo.setAmount(AmountCastUtil.castYuan2Fen(bodyParam.getString("amount")));

        switch (bodyParam.getString("trade_status")) {
            case "0":
                bo.setStatus(TransactionStatusEnum.TRANSACTING.getCode());
                break;
            case "1":
                bo.setStatus(TransactionStatusEnum.SUCCESS.getCode());
                break;
            case "2":
                bo.setStatus(TransactionStatusEnum.FAIL.getCode());
                break;
            case "15":
                bo.setStatus(TransactionStatusEnum.REFUNDED.getCode());
                break;
            default:
                bo.setStatus(TransactionStatusEnum.NOT_FOUNT.getCode());
        }

        bo.setBankChargeNo(bodyParam.getString("trade_no"));
        bo.setChannelChargeNo(bodyParam.getString("trade_no"));
        bo.setSuccessTime(DateUtil.strToLong(bodyParam.getString("finish_time")));
        bo.setMerchantId(bodyParam.getString("account_in"));
        return bo;
    }

    /**
     * 上传图片
     * @param batchUploadImgDto
     */
    private void batchUploadImg(BatchUploadImgDto batchUploadImgDto) throws PaymentException {
        long beginTime = System.currentTimeMillis();
        OpenParameters.Builder builder = new OpenParameters.Builder();
        builder.apiName("com.fbank.open-api.api.v1.allpay.batchUploadImg").param(batchUploadImgDto.getJSONStringParam());
        JSONObject response;

        try {
            response = JSON.parseObject(springClient.send(builder.build(), batchUploadImgDto.getMerchantNo()));
        }
        catch(Exception ex) {
            LOGGER.error("FuminAdapter#batchUploadImg batchUploadImg:{}", JSON.toJSONString(batchUploadImgDto), ex);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }

        long endTime = System.currentTimeMillis();
        LOGGER.info("FuminAdapter#batchUploadImg response:{} costTime:{}", response.toJSONString() , endTime - beginTime);
        Map<String, String> headParam = JSONUtils.parse2Map(response.getString("head"), String.class, String.class);

        if(headParam == null || !"000000".equals(headParam.get("rsp_code"))) {
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, FBankErrorMsgMappingEnum.convert(headParam.get("rsp_msg")));
        }
    }

    private QueryFuminRecordBo buildQueryFuminRecordBo(JSONObject body) {
        QueryFuminRecordBo queryFuminRecordBo = new QueryFuminRecordBo();
        queryFuminRecordBo.setPageNo(Integer.parseInt((String) body.get("page_no")));
        queryFuminRecordBo.setTotalSize(Integer.parseInt((String) body.get("total_size")));
        List<FuminRecordBo> records = new ArrayList<>();
        List list = (List) body.get("balance_records");
        if (list != null && list.size() > 0) {
            for (Object recordStr : list) {
                records.add(buildFuminRecordBo((JSONObject) recordStr));
            }
        }
        queryFuminRecordBo.setRecords(records);
        return queryFuminRecordBo;
    }


    private FuminRecordBo buildFuminRecordBo(JSONObject record) {
        FuminRecordBo fr = new FuminRecordBo();
        fr.setAccountNo(record.getString("account_no"));
        fr.setTradeDate(record.getString("trade_date"));
        fr.setTradeFlowNo(record.getString("trade_flow_no"));
        fr.setFlowNo(record.getString("flow_no"));
        fr.setAmount(record.getString("amount"));
        fr.setBalance(record.getString("balance"));
        fr.setDirection(record.getString("direction"));
        fr.setHandStatus(record.getString("hand_status"));
        fr.setTradeType(record.getString("trade_type"));
        fr.setAccountIn(record.getString("account_in"));
        fr.setAccountInName(record.getString("account_in_name"));
        fr.setAccountOut(record.getString("account_out"));
        fr.setAccountOutName(record.getString("account_out_name"));
        fr.setMemo(record.getString("memo"));
        return fr;
    }

    /**
     * @return void
     * @Description 查询用户交易信息
     * @Param [separateAccountDO]
     * @Author User
     * @Date 2021/6/3 上午11:43
     **/
    private TransactionInfoDto queryTransactionInfo(SeparateAccountDO separateAccountDO) throws PaymentException {
        LOGGER.info("FuminAdapter#queryTransactionInfo 入参信息：{} ", separateAccountDO);

        try {
            String merchantNo = bankConfigManager.getFuminBankMch(separateAccountDO.getBusinessOrderType());
            JSONObject jsonObject = new JSONObject();
            //商户号
            jsonObject.put("merchant_no", merchantNo);
            //日志流水号
            jsonObject.put("client_serial_no", leafUtil.getSnowflakeId());
            //原交易订单号
            jsonObject.put("biz_channel_orderid", separateAccountDO.getSeparateNo());
            OpenParameters.Builder builder = new OpenParameters.Builder();
            builder.apiName(ApiNameConstants.queryTransactionInfo)
                    .param(jsonObject.toJSONString());
            String response = springClient.send(builder.build(), merchantNo);
            LOGGER.info("FuminAdapter#queryTransactionInfo request:{} response:{} ", JSONUtils.toJSON(jsonObject), response);
            JSONObject responseParam = JSON.parseObject(response);
            Map<String, String> headParam = JSONUtils.parse2Map(responseParam.getString("head"), String.class, String.class);

            if(!"000000".equals(headParam.get("rsp_code"))) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, headParam.get("rsp_msg"));
            }

            String body = responseParam.getString("body");

            if(StringUtil.isEmpty(body)) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR, "FuminAdapter#queryTransactionInfo response body is null");
            }

            JSONObject bodyParam = JSONUtils.parse2Object(body, JSONObject.class);
            return buidTransactionInfoDto(bodyParam);

        }
        catch(PaymentException e) {
            LOGGER.error("FuminAdapter#queryTransactionInfo error separateAccountDO:" + JSON.toJSONString(separateAccountDO), e);
            throw e;
        }
        catch(Exception e) {
            LOGGER.error("FuminAdapter#queryTransactionInfo error separateAccountDO:" + JSON.toJSONString(separateAccountDO), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
    }

    private TransactionInfoDto buidTransactionInfoDto(JSONObject bodyParam) {
        TransactionInfoDto dto = new TransactionInfoDto();
        dto.setAccountIn(bodyParam.getString("account_in"));
        dto.setAccountOut(bodyParam.getString("account_out"));
        dto.setAmount(bodyParam.getString("amount"));
        dto.setCardId(bodyParam.getString("cust_id_no"));
        dto.setCardIdt(bodyParam.getString("cust_idt"));
        dto.setCreateTime(bodyParam.getString("create_time"));
        dto.setCurrency(bodyParam.getString("currency"));
        dto.setErrorDesc(bodyParam.getString("trade_error_code"));
        dto.setFinishTime(bodyParam.getString("finish_time"));
        dto.setNameIn(bodyParam.getString("name_in"));
        dto.setNameOut(bodyParam.getString("name_out"));
        dto.setPaymentCode(bodyParam.getString("payment_code"));
        dto.setPaymentMsg(bodyParam.getString("payment_msg"));
        dto.setTradeStatus(bodyParam.getString("trade_status"));
        dto.setTradeRemark(bodyParam.getString("trade_remark"));
        dto.setPhoneNo(bodyParam.getString("phone_no"));
        dto.setSubTradeType(bodyParam.getString("sub_trade_type"));
        dto.setTradeNo(bodyParam.getString("trade_no"));
        return dto;
    }

    /**
     * 获取收款人信息
     * @param accountId
     * @return
     */
    private String getPayeeAccountId(String accountId) throws PaymentException {
        if(StringUtils.isBlank(accountId)) {
            return accountId;
        }

        PayAccount payAccount =  payAccountService.getPayAccountByAccountId(accountId);

        try {
            if(BusinessIdTypeEnum.EC_POP.getType().equals(payAccount.getBusinessIdType())) {
                // 进件完成
                if(accountService.checkOpenAccountComplete(accountId)) {
                    return accountId;
                }
                // 开基本户完成
                else if(accountService.checkBaseOpenAccountComplete(accountId)) {
                    return ApolloUtil.getProperty(Constants.FBANK_YBM_TECH_ACCOUNT_ID);
                }

                throw new PaymentException("UnsupportedOperationException. Not open base account.");
            }
            else if(BusinessIdTypeEnum.YIKUAIQIAN_FM.getType().equals(payAccount.getBusinessIdType())) {
                return ApolloUtil.getProperty(Constants.FBANK_HEYE_O2O_ACCOUNT_ID);
            }
            else if(BusinessIdTypeEnum.YIKUAIQIAN_DOC_GK.getType().equals(payAccount.getBusinessIdType())) {
                return ApolloUtil.getProperty(Constants.FBANK_HEYE_GK_ACCOUNT_ID);
            }

            return accountId;
        }
        catch(Exception ex) {
            LOGGER.error(ex.getMessage() + ", accountId=" +  accountId, ex);
            throw ex;
        }
    }

    /**
     * 获取退款人信息
     * @param payNo
     * @return
     */
    private String getRefundAccountId(String payNo, String payChannel) throws PaymentException {
        PayResultOrder payResultOrder = payResultOrderService.selectByPayNoAndChannel(payNo, payChannel);

        if(payResultOrder == null) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }

        return StringUtils.isBlank(payResultOrder.getMerchantId()) ? payResultOrder.getReceiveId() :
                thirdUserInfoService.getThirdUserInfoByThirdUserId(payResultOrder.getMerchantId()).getUserId();
    }

    /**
     * Mock场景
     * fbank_mock_pay_account_mapping_
     * @param accountId
     * @return
     */
    private String getAdapterAccountId(String accountId) {
        if(StringUtils.isBlank(accountId)) {
            return accountId;
        }

        if(accountId.contains(PayAccount.SUFFIX_DELETE)) {
            accountId = accountId.substring(NumberUtils.INTEGER_ZERO,  accountId.indexOf(PayAccount.SUFFIX_DELETE));
        }

        try {
            String mappingAccountId = ApolloUtil.getProperty("fbank_mock_pay_account_mapping_" + accountId);
            LOGGER.info("------------fbank_mock_pay_account_mapping_" + accountId + "|" + mappingAccountId);
            return !StringUtils.isBlank(mappingAccountId) ? mappingAccountId : accountId;
        }
        catch(Exception ex) {
            LOGGER.error(ex.getMessage() + ", accountId=" +  accountId, ex);
        }

        return accountId;
    }

    /**
     * 职业编码
     */
    private String castPersonProfession(String profession) {
        if (profession.equals(ProfessionEnum.LEADER.getType())) {
            return "0";
        }
        if (profession.equals(ProfessionEnum.TECHNOLOGY.getType())) {
            return "3";
        }
        if (profession.equals(ProfessionEnum.SOLDIER.getType())) {
            return "M";
        }
        if (profession.equals(ProfessionEnum.OTHER.getType())) {
            return "D";
        }
        return profession.replaceAll("0", "");
    }

    /**
     * 获取图片并压缩
     */
    private String getPicBase64(UserDto userDto, String code) {
        LOGGER.info("FuminAdapter#getPicBase64 load pic begin url:{}", userDto.getPicMap().get(code));
        String basePic = PicUtil.resizeImg(HttpClientUtil.getStream(userDto.getPicMap().get(code)), "jpg");
        LOGGER.info("FuminAdapter#getPicBase64 load pic end url:{}", userDto.getPicMap().get(code));
        return basePic;
    }

    /**
     * 开户存储虚户号
     */
    private void saveThirdUserId(String userId, String thirdUserId) {
        int count = userService.updateThirdUserIdByUserId(userId, Constants.QIFUTONG, thirdUserId);

        if(count < 1) {
            LOGGER.error("FuminAdapter#saveThirdUserId save failed userId:{} thirdUserId:{}", userId, thirdUserId);
        }
    }
}
